use core::alloc::Layout;
use alloc::alloc::{alloc, dealloc};
use utils::MutCell;

#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
    Timeout,
    Resource,
    Memory,
    New,
}

mod event_flags;
mod mutex;
mod thread;
mod task;
mod message_queue;
mod delay_queue;
pub use message_queue::MessageQueue;
pub use thread::Thread;
pub use event_flags::EventFlags;
pub use mutex::{RawMutex, Mutex};
pub use delay_queue::DelayQueue;
pub use task::Task;

use crate::osWaitForever;

const FLAG_LOCK: u32 = 0x01;
const FLAG_NOTIFY: u32 = 0x02;

pub struct CondVar<T> {
    flags: EventFlags,
    value: MutCell<Option<T>>,
}

impl<T> CondVar<T> {

    pub fn new(name: &'static str) -> Self {
        let flags = EventFlags::new(name);
        flags.set_flags(FLAG_LOCK);
        Self { flags, value: MutCell::const_new(None) }
    }

    pub fn notify(&self, value: T) {
        self.lock();
        unsafe { *self.value.as_mut() = Some(value); }
        self.flags.set_flags(FLAG_NOTIFY);
        self.unlock();
    } 

    pub fn wait_notify(&self) -> T {
        loop {
            self.lock();
            let value = unsafe { self.value.as_mut().take() };
            if let Some(value) = value {
                return value;
            }
            self.unlock();
            _ = self.flags.wait_any_flags(FLAG_NOTIFY, osWaitForever);
        }
    }

    pub fn wait(&mut self, timeout: u32) -> Option<T> {
        self.lock();
        if self.value.is_none() {
            self.unlock();
            _ = self.flags.wait_any_flags(FLAG_NOTIFY, timeout);
            self.lock();
        }
        let value = unsafe { self.value.as_mut().take() };
        self.unlock();
        value
    }

    fn lock(&self) {
        _ = self.flags.wait_any_flags(FLAG_LOCK, osWaitForever);
    }

    fn unlock(&self) {
        self.flags.set_flags(FLAG_LOCK);
    }

}

unsafe impl Send for DynamicMem {}
unsafe impl Sync for DynamicMem {}

struct DynamicMem {
    ptr: *mut u8,
    layout: Layout,
}

impl DynamicMem {

    pub fn new(len: usize) -> Self {
        unsafe {
            let layout = Layout::from_size_align_unchecked(len, 8);
            let ptr = alloc(layout);
            if ptr.is_null() {
                panic!("alloc ptr is null")
            }
            Self { ptr, layout }
        }
    }

    pub fn new2(len: usize) -> Result<Self, Error> {
        unsafe {
            let layout = Layout::from_size_align_unchecked(len, 8);
            let ptr = alloc(layout);
            if ptr.is_null() {
                Err(Error::Memory)
            } else {
                Ok(Self { ptr, layout })
            }
        }
    }
}

impl Drop for DynamicMem {

    fn drop(&mut self) {
        unsafe { dealloc(self.ptr, self.layout); }
    }
}









